home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Plus
/
Graphics Plus.iso
/
msdos
/
raytrace
/
pov
/
bin
/
xtras
/
addon1.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-09-11
|
55KB
|
2,440 lines
/****************************************************************************
*
* ATTENTION!!!
*
* THIS FILE HAS BEEN MODIFIED!!! IT IS NOT PART OF THE OFFICAL
* POV-RAY 2.2 DISTRIBUTION!!!
*
* THIS FILE IS PART OF "FASTER THAN POV-RAY" (VERSION 2.2),
* A SPED-UP VERSION OF POV-RAY 2.2. USE AT YOUR OWN RISK!!!!!!
*
* New files: addon0.c, addon1.c, addon2.c, addon3.c, addon.h
*
* The additional modules were written by Dieter Bayer.
*
* Send comments, suggestions, bugs, ideas ... to:
*
* e-mail: dieter@cip.e-technik.uni-erlangen.de
* CIS: 100255.3074
*
* All changed/added lines are enclosed in #ifdef DB_CODE ... #endif
*
* The vista projection was taken from:
*
* A. Hashimoto, T. Akimoto, K. Mase, and Y. Suenaga,
* "Vista Ray-Tracing: High Speed Ray Tracing Using Perspective
* Projection Image", New Advances in Computer Graphics, Proceedings
* of CG International '89, R. A. Earnshaw, B. Wyvill (Eds.),
* Springer, ..., pp. 549-560
*
* The idea for the light buffer was taken from:
*
* E. Haines and D. Greenberg, "The Light Buffer: A Shadow-Testing
* Accelerator", IEEE CG&A, Vol. 6, No. 9, Sept. 1986, pp. 6-16
*
*****************************************************************************/
/****************************************************************************
* addon1.c
*
* This module was written by Dieter Bayer.
*
* This module contains functions used only for the vista buffer.
*
* 01.03.1994 : Creation
*
* 29.04.1994 : Version 2.0
*
******************************************************************************/
#include <time.h>
#include "frame.h"
#include "vector.h"
#include "povproto.h"
#include "addon.h"
#ifdef DB_CODE
/* count project tests */
#define COUNT_PROJECT_TESTS
#define NUMBER_OF_TYPES 18
#define TYPE_BICUBIC_PATCH 0
#define TYPE_BLOB 1
#define TYPE_BOX 2
#define TYPE_CONE 3
#define TYPE_CSG_INTERSECTION 4
#define TYPE_CSG_MERGE 5
#define TYPE_CSG_UNION 6
#define TYPE_DISC 7
#define TYPE_ELLIPSOID 8
#define TYPE_HEIGHT_FIELD 9
#define TYPE_LIGHT_SOURCE 10
#define TYPE_PLANE 11
#define TYPE_POLY 12
#define TYPE_QUADRIC 13
#define TYPE_SMOOTH_TRIANGLE 14
#define TYPE_SPHERE 15
#define TYPE_TRIANGLE 16
#define TYPE_UNKNOWN 17
#define NUMBER_OF_FLAGS 9
#define FLAG_SUM 0
#define FLAG_INFINITE 1
#define FLAG_SCREEN_FILLING 2
#define FLAG_USED_IN_CSG 3
#define FLAG_INVISIBLE 4
#define FLAG_BOUNDED 5
#define FLAG_CLIPPED 6
#define FLAG_BOUND_OBJECT 7
#define FLAG_CLIP_OBJECT 8
/*****************************************************************************
* external variables
******************************************************************************/
extern long Number_Of_Rays;
extern FRAME Frame;
extern int Trace_Level;
extern int Use_Slabs;
extern DBL Max_Trace_Level;
extern time_t tstart, tstop;
extern unsigned int Options;
extern OBJECT *Root_Object;
extern METHODS Bicubic_Patch_Methods;
extern METHODS Blob_Methods;
extern METHODS Box_Methods;
extern METHODS Cone_Methods;
extern METHODS Csg_Height_Field_Methods;
extern METHODS CSG_Intersection_Methods;
extern METHODS CSG_Merge_Methods;
extern METHODS CSG_Union_Methods;
extern METHODS Disc_Methods;
extern METHODS Ellipsoid_Methods;
extern METHODS Height_Field_Methods;
extern METHODS Light_Source_Methods;
extern METHODS Plane_Methods;
extern METHODS Poly_Methods;
extern METHODS Quadric_Methods;
extern METHODS Smooth_Triangle_Methods;
extern METHODS Sphere_Methods;
extern METHODS Triangle_Methods;
extern unsigned MAXQUEUE;
extern unsigned long totalQueues, totalQueueResets, nChecked, nEnqueued;
extern size_t Mem_Vista_Buffer;
/*****************************************************************************
* Non-static variables
******************************************************************************/
PROJECT_TREE_NODE *Root_Vista;
unsigned long Project_Tests = 0, Project_Tests_Succeeded = 0;
unsigned Project_Algorithm = PROJECTIONS_WITH_LEAF_SLABS;
/*****************************************************************************
* Static variables
******************************************************************************/
static DBL distance;
static MATRIX WC2VC, WC2VCinv;
static VECTOR O, U, V, W;
/* Planes for 3d-clipping */
static VECTOR VIEW_VX1 = {-0.8944271910, 0.0, -0.4472135955};
static VECTOR VIEW_VX2 = { 0.8944271910, 0.0, -0.4472135955};
static VECTOR VIEW_VY1 = {0.0, -0.8944271910, -0.4472135955};
static VECTOR VIEW_VY2 = {0.0, 0.8944271910, -0.4472135955};
static DBL VIEW_DX1 = 0.4472135955;
static DBL VIEW_DX2 = 0.4472135955;
static DBL VIEW_DY1 = 0.4472135955;
static DBL VIEW_DY2 = 0.4472135955;
/* Queues */
static Qelem *Tree_Leaf_Queue;
static PROJECT_TREE_NODE **Tree_Node_Queue;
/*****************************************************************************
* Static functions
******************************************************************************/
static void Project_rectangle PARAMS((PROJECT *Project, VECTOR P1, VECTOR P2, VECTOR P3, VECTOR P4, int *visible));
static void Project_triangle PARAMS((PROJECT *Project, VECTOR P1, VECTOR P2, VECTOR P3, int *visible));
static void Project_Bbox PARAMS((PROJECT *Project, VECTOR *Points, int *visible));
static void Project_Bounds PARAMS((PROJECT *Project, BBOX *Bounds, int *visible));
static void Get_Perspective_Projection PARAMS((OBJECT *Object, PROJECT *Project, int infinite));
static void Project_Object PARAMS((OBJECT *Object, PROJECT *Project));
static void Project_Bicubic_Patch PARAMS((PROJECT *Project, OBJECT *Object));
static void Project_Box PARAMS((PROJECT *Project, OBJECT *Object, int *visible));
static void Project_Height_Field PARAMS((PROJECT *Project, OBJECT *Object, int *visible));
static void Project_Sphere PARAMS((PROJECT *Project, OBJECT *Object));
static void Project_Triangle PARAMS((PROJECT *Project, OBJECT *Object, int *visible));
static void Project_Smooth_Triangle PARAMS((PROJECT *Project, OBJECT *Object, int *visible));
static void Get_Ellipse_Projection PARAMS((PROJECT *Project, DBL a20, DBL a02, DBL a11, DBL a10, DBL a01, DBL a00));
static void Transform_Point PARAMS((VECTOR *P));
static void Project_Bounding_Slab PARAMS((PROJECT *Project, PROJECT_TREE_NODE **Tree, OBJECT *Object));
static void Create_Rayinfo PARAMS((RAY *Ray, RAYINFO *rayinfo));
static int Intersect_Vista_Tree PARAMS((RAY *Ray, PROJECT_TREE_NODE *Tree, int x, INTERSECTION *Best_Intersection));
/*****************************************************************************
*
* FUNCTION : Init_Project_Tree_Queues
*
* ARGUMENTS : none
*
* MODIFIED ARGS : none
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Init queues for descending project tress, i.e. allocate memory needed.
*
* CHANGES
*
* -
*
******************************************************************************/
void Init_Project_Tree_Queues()
{
Tree_Leaf_Queue = (Qelem *)VB_malloc(MAXQUEUE*sizeof(Qelem));
if (Tree_Leaf_Queue == NULL)
{
Fatal_MAError("priority queue for vista/light buffer");
}
Tree_Node_Queue = (PROJECT_TREE_NODE **)VB_malloc(MAXQUEUE*sizeof(PROJECT_TREE_NODE *));
if (Tree_Node_Queue == NULL)
{
Fatal_MAError("priority queue for vista/light buffer");
}
}
/*****************************************************************************
*
* FUNCTION : Prune_Vista_Tree
*
* ARGUMENTS : y - Current scanline number
*
* MODIFIED ARGS : none
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Prune vista tree, i.e. mark all nodes not on the current line inactive.
*
* CHANGES
*
* -
*
******************************************************************************/
void Prune_Vista_Tree(y)
int y;
{
unsigned size;
unsigned short i;
PROJECT_TREE_NODE *Node, *Sib;
size = 0;
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((y < Root_Vista->Project.y1) || (y > Root_Vista->Project.y2))
{
/* Root doesn't lie on current line --> prune root */
Root_Vista->is_leaf |= PRUNE_TEMPORARY;
}
else
{
/* Root lies on current line --> unprune root */
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
Root_Vista->is_leaf &= PRUNE_TEMPORARY_INVERS;
Tree_Node_Queue[size++] = Root_Vista;
}
while (size > 0)
{
Node = Tree_Node_Queue[--size];
if (Node->is_leaf & TRUE)
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((y < Node->Project.y1) || (y > Node->Project.y2))
{
/* Leaf doesn't lie on current line --> prune leaf */
Node->is_leaf |= PRUNE_TEMPORARY;
}
else
{
/* Leaf lies on current line --> unprune leaf */
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
Node->is_leaf &= PRUNE_TEMPORARY_INVERS;
}
}
else
{
/* Check siblings of the node */
for (i = 0; i < Node->Entries; i++)
{
Sib = Node->Entry[i];
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((y < Sib->Project.y1) || (y > Sib->Project.y2))
{
/* Sibling doesn't lie on current line --> prune sibling */
Sib->is_leaf |= PRUNE_TEMPORARY;
}
else
{
/* Sibling lies on current line --> unprune sibling */
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
Sib->is_leaf &= PRUNE_TEMPORARY_INVERS;
/* Add sibling to list */
Tree_Node_Queue[size++] = Sib;
}
}
}
}
}
/*****************************************************************************
*
* FUNCTION : Trace_Primary_Ray
*
* ARGUMENTS : Ray - Current ray
* Colour - Ray's colour
* x - Current x-coordinate
*
* MODIFIED ARGS : colour
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Trace a primary ray using the vista tree.
*
* CHANGES
*
* -
*
******************************************************************************/
void Trace_Primary_Ray (Ray, Colour, x)
RAY *Ray;
COLOUR *Colour;
int x;
{
INTERSECTION Best_Intersection;
COOPERATE
Number_Of_Rays++;
if (Trace_Level > (int)Max_Trace_Level)
return;
Colour->Red = Colour->Green = Colour->Blue = 0.0;
Best_Intersection.Depth = BOUND_HUGE;
/* What objects does this ray intersect? */
if (Intersect_Vista_Tree(Ray, Root_Vista, x, &Best_Intersection))
{
Determine_Apparent_Colour (&Best_Intersection, Colour, Ray);
}
else
{
if (Frame.Fog_Distance > 0.0)
{
*Colour = Frame.Fog_Colour;
}
else
{
*Colour = Frame.Background_Colour;
}
}
}
/*****************************************************************************
*
* FUNCTION : Create_Rayinfo
*
* ARGUMENTS : Ray - Current ray
* rayinfo - Rayinfo structure
*
* MODIFIED ARGS : rayinfo
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Calculate the rayinfo structure for a given ray. It's need for
* intersection the ray with bounding boxes.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Create_Rayinfo(Ray, rayinfo)
RAY *Ray;
RAYINFO *rayinfo;
{
DBL t;
/* Create the direction vectors for this ray */
rayinfo->slab_num = Ray->Initial;
if ((rayinfo->nonzero.x = ((t = Ray->Direction.x) != 0.0)) != 0)
{
rayinfo->slab_den.x = 1.0 / t;
rayinfo->positive.x = (Ray->Direction.x > 0.0);
}
if ((rayinfo->nonzero.y = ((t = Ray->Direction.y) != 0.0)) != 0)
{
rayinfo->slab_den.y = 1.0 / t;
rayinfo->positive.y = (Ray->Direction.y > 0.0);
}
if ((rayinfo->nonzero.z = ((t = Ray->Direction.z) != 0.0)) != 0)
{
rayinfo->slab_den.z = 1.0 / t;
rayinfo->positive.z = (Ray->Direction.z > 0.0);
}
}
/*****************************************************************************
*
* FUNCTION : Intersect_Vista_Tree
*
* ARGUMENTS : Ray - Primary ray
* Tree - Vista tree's top-node
* x - Current x-coordinate
* Best_Intersection - Intersection found
*
* MODIFIED ARGS : Best_Intersection
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Intersect a PRIMARY ray with the vista tree
* (tree pruning is used can be primary ray!!!).
*
* CHANGES
*
* -
*
******************************************************************************/
static int Intersect_Vista_Tree(Ray, Tree, x, Best_Intersection)
RAY *Ray;
PROJECT_TREE_NODE *Tree;
int x;
INTERSECTION *Best_Intersection;
{
INTERSECTION New_Intersection;
unsigned short i;
unsigned Qsize, size;
int Found;
RAYINFO rayinfo;
DBL key;
OBJECT *Object;
PROJECT_TREE_NODE *Node;
/* Start with an empty priority queue */
Qsize = 0;
Found = FALSE;
totalQueueResets++;
/* Which algorithm to use? */
switch (Project_Algorithm)
{
/************************************************************************
* Do not use any bounding slab tests. just use their projections.
*************************************************************************/
case PROJECTIONS_WITHOUT_SLABS :
/* Check root */
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((x >= Tree->Project.x1) && (x <= Tree->Project.x2))
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
Tree_Node_Queue[Qsize++] = Tree;
}
while (Qsize > 0)
{
Tree = Tree_Node_Queue[--Qsize];
switch (Tree->is_leaf)
{
case FALSE:
/* Check siblings of the unpruned node in 2d */
for (i = 0; i < Tree->Entries; i++)
{
Node = Tree->Entry[i];
/* Check unpruned siblings only */
if (Node->is_leaf < PRUNE_CHECK)
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((x >= Node->Project.x1) && (x <= Node->Project.x2))
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
/* Add node to node queue */
if (Qsize >= MAXQUEUE)
Fatal_Error("Node queue overflow.\n");
Tree_Node_Queue[Qsize++] = Node;
}
}
}
break;
case TRUE:
/* Unpruned leaf --> test object */
if (Intersection(&New_Intersection, ((PROJECT_TREE_LEAF *)Tree)->Object, Ray))
{
if (New_Intersection.Depth < Best_Intersection->Depth)
{
*Best_Intersection = New_Intersection;
Found = TRUE;
}
}
break;
/* default:
The node/leaf is pruned and needn't be checked */
}
}
break;
/************************************************************************
* Use bounding slab tests for leaves (i.e. objects) in the tree.
*************************************************************************/
case PROJECTIONS_WITH_LEAF_SLABS :
/* Create the direction vectors for this ray */
Create_Rayinfo(Ray, &rayinfo);
/* Fill the priority queue with all possible candidates */
size = 0;
/* Check root */
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((x >= Tree->Project.x1) && (x <= Tree->Project.x2))
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
Tree_Node_Queue[size++] = Tree;
}
while (size > 0)
{
Tree = Tree_Node_Queue[--size];
switch (Tree->is_leaf)
{
case FALSE:
/* Check siblings of the unpruned node in 2d */
for (i = 0; i < Tree->Entries; i++)
{
Node = Tree->Entry[i];
/* Check unpruned siblings only */
if (Node->is_leaf < PRUNE_CHECK)
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((x >= Node->Project.x1) && (x <= Node->Project.x2))
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
/* add node to node queue */
if (size >= MAXQUEUE)
Fatal_Error("Node queue overflow.\n");
Tree_Node_Queue[size++] = Node;
}
}
}
break;
case TRUE:
/* Unpruned leaf --> test object's bounding box in 3d */
CheckAndEnqueue(Tree_Leaf_Queue, &Qsize, ((PROJECT_TREE_LEAF *)Tree)->Object,
&((PROJECT_TREE_LEAF *)Tree)->Object->Bounds, &rayinfo);
break;
/* default:
The node/leaf is pruned and needn't be checked */
}
}
/* Now test the candidates in the priority queue */
while (Qsize > 0)
{
PriorityQueueDelete(Tree_Leaf_Queue, &Qsize, &key, &Object);
if (key > Best_Intersection->Depth)
break;
if (Intersection(&New_Intersection, Object, Ray))
{
if (New_Intersection.Depth < Best_Intersection->Depth)
{
*Best_Intersection = New_Intersection;
Found = TRUE;
}
}
}
break;
/************************************************************************
* Use bounding slab tests for all nodes that pass the projection test.
*************************************************************************/
case PROJECTIONS_WITH_NODE_SLABS :
/* Create the direction vectors for this ray */
Create_Rayinfo(Ray, &rayinfo);
/* Check root */
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((x >= Tree->Project.x1) && (x <= Tree->Project.x2))
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
CheckAndEnqueue(Tree_Leaf_Queue, &Qsize, (OBJECT *)Tree,
&Tree->Object->Bounds, &rayinfo);
}
while (Qsize > 0)
{
PriorityQueueDelete(Tree_Leaf_Queue, &Qsize, &key, &Object);
if (key > Best_Intersection->Depth)
break;
Tree = (PROJECT_TREE_NODE *)Object;
switch (Tree->is_leaf)
{
case FALSE:
/* Check siblings of the unpruned node in 2d */
for (i = 0; i < Tree->Entries; i++)
{
Node = Tree->Entry[i];
/* Check unpruned siblings only */
if (Node->is_leaf < PRUNE_CHECK)
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((x >= Node->Project.x1) && (x <= Node->Project.x2))
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
CheckAndEnqueue(Tree_Leaf_Queue, &Qsize, (OBJECT *)Node,
&Node->Object->Bounds, &rayinfo);
}
}
}
break;
case TRUE:
/* Unpruned leaf --> test object */
if (Intersection(&New_Intersection, ((PROJECT_TREE_LEAF *)Tree)->Object, Ray))
{
if (New_Intersection.Depth < Best_Intersection->Depth)
{
*Best_Intersection = New_Intersection;
Found = TRUE;
}
}
break;
/* default:
The node/leaf is pruned and needn't be checked */
}
}
break;
default :
Fatal_Error("Unkown algorithm.\n");
}
return(Found);
}
/*****************************************************************************
*
* FUNCTION : Intersect_Light_Tree
*
* ARGUMENTS : Ray - Shadow ray
* Tree - Light tree's top-node
* x - X-coordinate of the shadow ray
* y - Y-coordinate of the shadow ray
* Best_Intersection - Intersection found
* Best_Object - Object found
*
* MODIFIED ARGS : Best_Intersection, Best_Object
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Intersect a shadow ray with the light tree
* (same as for the vista tree but without pruning).
*
* CHANGES
*
* -
*
******************************************************************************/
int Intersect_Light_Tree(Ray, Tree, x, y, Best_Intersection, Best_Object)
RAY *Ray;
PROJECT_TREE_NODE *Tree;
int x, y;
INTERSECTION *Best_Intersection;
OBJECT **Best_Object;
{
INTERSECTION New_Intersection;
unsigned short i;
unsigned Qsize, size;
int Found;
RAYINFO rayinfo;
DBL key;
OBJECT *Object;
PROJECT_TREE_NODE *Node;
/* Start with an empty priority queue */
Qsize = 0;
Found = FALSE;
totalQueueResets++;
/* which algorithm to use? */
switch (Project_Algorithm)
{
/************************************************************************
* Do not use any bounding slab tests. just use their projections.
*************************************************************************/
case PROJECTIONS_WITHOUT_SLABS :
/* Check root */
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((x >= Tree->Project.x1) && (x <= Tree->Project.x2) &&
(y >= Tree->Project.y1) && (y <= Tree->Project.y2))
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
Tree_Node_Queue[Qsize++] = Tree;
}
while (Qsize > 0)
{
Tree = Tree_Node_Queue[--Qsize];
if (Tree->is_leaf)
{
/* Leaf --> test object */
if (Intersection(&New_Intersection, ((PROJECT_TREE_LEAF *)Tree)->Object, Ray))
{
if (New_Intersection.Depth < Best_Intersection->Depth)
{
*Best_Intersection = New_Intersection;
*Best_Object = ((PROJECT_TREE_LEAF *)Tree)->Object;
Found = TRUE;
}
}
}
else
{
/* Check siblings of the node in 2d */
for (i = 0; i < Tree->Entries; i++)
{
Node = Tree->Entry[i];
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((x >= Node->Project.x1) && (x <= Node->Project.x2) &&
(y >= Node->Project.y1) && (y <= Node->Project.y2))
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
/* Add node to node queue */
if (Qsize >= MAXQUEUE)
Fatal_Error("Node queue overflow.\n");
Tree_Node_Queue[Qsize++] = Node;
}
}
}
}
break;
/************************************************************************
* Use bounding slab tests for leaves (i.e. objects) in the tree.
*************************************************************************/
case PROJECTIONS_WITH_LEAF_SLABS :
/* Create the direction vectors for this ray */
Create_Rayinfo(Ray, &rayinfo);
/* Fill the priority queue with all possible candidates */
size = 0;
/* Check root */
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((x >= Tree->Project.x1) && (x <= Tree->Project.x2) &&
(y >= Tree->Project.y1) && (y <= Tree->Project.y2))
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
Tree_Node_Queue[size++] = Tree;
}
while (size > 0)
{
Tree = Tree_Node_Queue[--size];
if (Tree->is_leaf)
{
/* Leaf --> test object's bounding box in 3d */
CheckAndEnqueue(Tree_Leaf_Queue, &Qsize, ((PROJECT_TREE_LEAF *)Tree)->Object,
&((PROJECT_TREE_LEAF *)Tree)->Object->Bounds, &rayinfo);
}
else
{
/* Check siblings of the node in 2d */
for (i = 0; i < Tree->Entries; i++)
{
Node = Tree->Entry[i];
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((x >= Node->Project.x1) && (x <= Node->Project.x2) &&
(y >= Node->Project.y1) && (y <= Node->Project.y2))
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
/* Add node to node queue */
if (size >= MAXQUEUE)
Fatal_Error("Node queue overflow.\n");
Tree_Node_Queue[size++] = Node;
}
}
}
}
/* Now test the candidates in the priority queue */
while (Qsize > 0)
{
PriorityQueueDelete(Tree_Leaf_Queue, &Qsize, &key, &Object);
if (key > Best_Intersection->Depth)
break;
if (Intersection(&New_Intersection, Object, Ray))
{
if (New_Intersection.Depth < Best_Intersection->Depth)
{
*Best_Intersection = New_Intersection;
*Best_Object = Object;
Found = TRUE;
}
}
}
break;
/************************************************************************
* Use bounding slab tests for all nodes that pass the projection test.
*************************************************************************/
case PROJECTIONS_WITH_NODE_SLABS :
/* Create the direction vectors for this ray */
Create_Rayinfo(Ray, &rayinfo);
/* Check root */
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((x >= Tree->Project.x1) && (x <= Tree->Project.x2) &&
(y >= Tree->Project.y1) && (y <= Tree->Project.y2))
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
CheckAndEnqueue(Tree_Leaf_Queue, &Qsize, (OBJECT *)Tree,
&Tree->Object->Bounds, &rayinfo);
}
while (Qsize > 0)
{
PriorityQueueDelete(Tree_Leaf_Queue, &Qsize, &key, &Object);
if (key > Best_Intersection->Depth)
break;
Tree = (PROJECT_TREE_NODE *)Object;
if (Tree->is_leaf)
{
/* Leaf --> test object */
if (Intersection(&New_Intersection, ((PROJECT_TREE_LEAF *)Tree)->Object, Ray))
{
if (New_Intersection.Depth < Best_Intersection->Depth)
{
*Best_Intersection = New_Intersection;
*Best_Object = ((PROJECT_TREE_LEAF *)Tree)->Object;
Found = TRUE;
}
}
}
else
{
/* Check siblings of the unpruned node in 2d */
for (i = 0; i < Tree->Entries; i++)
{
Node = Tree->Entry[i];
#ifdef COUNT_PROJECT_TESTS
Project_Tests++;
#endif
if ((x >= Node->Project.x1) && (x <= Node->Project.x2) &&
(y >= Node->Project.y1) && (y <= Node->Project.y2))
{
#ifdef COUNT_PROJECT_TESTS
Project_Tests_Succeeded++;
#endif
CheckAndEnqueue(Tree_Leaf_Queue, &Qsize, (OBJECT *)Node,
&Node->Object->Bounds, &rayinfo);
}
}
}
}
break;
default :
Fatal_Error("Unkown algorithm.\n");
}
return(Found);
}
/*****************************************************************************
*
* FUNCTION : Project_triangle
*
* ARGUMENTS : Project - Triangle's projection
* P1, P2, P3 - Triangle's edges
* visible - Flag if triangle is visible
*
* MODIFIED ARGS : Project, visible
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Project a triangle onto the screen.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Project_triangle (Project, P1, P2, P3, visible)
PROJECT *Project;
VECTOR P1, P2, P3;
int *visible;
{
VECTOR Points[MAX_CLIP_POINTS];
int i, number;
int x, y;
Points[0] = P1;
Points[1] = P2;
Points[2] = P3;
number = 3;
/* Clip triangle only if some quick tests say it's necessary.
Assuming that only a few triangles need clipping this saves some time.
(I don't need to write fabs(1+P?.z) since the tests succeed anyway if
P?.z < -1. Hope the compiler doesn't change the tests' order!) */
if ((P1.z < -1.0) || (P2.z < -1.0) || (P3.z < -1.0) ||
(fabs(P1.x) > 0.5*(1.0+P1.z)) || (fabs(P1.y) > 0.5*(1.0+P1.z)) ||
(fabs(P2.x) > 0.5*(1.0+P2.z)) || (fabs(P2.y) > 0.5*(1.0+P2.z)) ||
(fabs(P3.x) > 0.5*(1.0+P3.z)) || (fabs(P3.y) > 0.5*(1.0+P3.z)))
{
Clip_Polygon(Points, &number, &VIEW_VX1, &VIEW_VX2, &VIEW_VY1, &VIEW_VY2,
VIEW_DX1, VIEW_DX2, VIEW_DY1, VIEW_DY2);
}
if (!number)
return;
for (i = 0; i < number; i++)
{
if (Points[i].z == -1.0)
{
Points[i].x = Points[i].y = 0.0;
}
else
{
Points[i].x = Points[i].x / (1.0 + Points[i].z);
Points[i].y = Points[i].y / (1.0 + Points[i].z);
}
x = Frame.Screen_Width/2 + (int)(Frame.Screen_Width * Points[i].x);
y = Frame.Screen_Height/2 - (int)(Frame.Screen_Height * Points[i].y);
if (x < Project->x1) Project->x1 = x;
if (x > Project->x2) Project->x2 = x;
if (y < Project->y1) Project->y1 = y;
if (y > Project->y2) Project->y2 = y;
}
*visible = TRUE;
}
/*****************************************************************************
*
* FUNCTION : Project_rectangle
*
* ARGUMENTS : Project - Rectangle's projection
* P1, P2, P3, P4 - Rectangle's edges
* visible - Flag if rectangle is visible
*
* MODIFIED ARGS : Project, visible
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Project a rectangle onto the screen.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Project_rectangle(Project, P1, P2, P3, P4, visible)
PROJECT *Project;
VECTOR P1, P2, P3, P4;
int *visible;
{
VECTOR Points[MAX_CLIP_POINTS];
int i, number;
int x, y;
Points[0] = P1;
Points[1] = P2;
Points[2] = P3;
Points[3] = P4;
number = 4;
/* Clip square only if some quick tests say it's necessary.
Assuming that only a few squares need clipping this saves some time.
(I don't need to write fabs(1+P?.z) since the tests succeed anyway if
P?.z < -1. Hope the compiler doesn't change the tests' order!) */
if ((P1.z < -1.0) || (P2.z < -1.0) || (P3.z < -1.0) || (P4.z < -1.0) ||
(fabs(P1.x) > 0.5*(1.0+P1.z)) || (fabs(P1.y) > 0.5*(1.0+P1.z)) ||
(fabs(P2.x) > 0.5*(1.0+P2.z)) || (fabs(P2.y) > 0.5*(1.0+P2.z)) ||
(fabs(P3.x) > 0.5*(1.0+P3.z)) || (fabs(P3.y) > 0.5*(1.0+P3.z)) ||
(fabs(P4.x) > 0.5*(1.0+P4.z)) || (fabs(P4.y) > 0.5*(1.0+P4.z)))
{
Clip_Polygon(Points, &number, &VIEW_VX1, &VIEW_VX2, &VIEW_VY1, &VIEW_VY2,
VIEW_DX1, VIEW_DX2, VIEW_DY1, VIEW_DY2);
}
if (!number)
return;
for (i = 0; i < number; i++)
{
if (Points[i].z == -1.0)
{
Points[i].x = Points[i].y = 0.0;
}
else
{
Points[i].x = Points[i].x / (1.0 + Points[i].z);
Points[i].y = Points[i].y / (1.0 + Points[i].z);
}
x = Frame.Screen_Width/2 + (int)(Frame.Screen_Width * Points[i].x);
y = Frame.Screen_Height/2 - (int)(Frame.Screen_Height * Points[i].y);
if (x < Project->x1) Project->x1 = x;
if (x > Project->x2) Project->x2 = x;
if (y < Project->y1) Project->y1 = y;
if (y > Project->y2) Project->y2 = y;
}
*visible = TRUE;
}
/*****************************************************************************
*
* FUNCTION : Project_BBox
*
* ARGUMENTS : Project - Box's projection
* Points - Box's edges
* visible - Flag if box is visible
*
* MODIFIED ARGS : Project, visible
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Project a box onto the screen.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Project_Bbox(Project, Points, visible)
PROJECT *Project;
VECTOR *Points;
int *visible;
{
int vis;
PROJECT New;
New.x1 = MAX_VB_ENTRY;
New.x2 = MIN_VB_ENTRY;
New.y1 = MAX_VB_ENTRY;
New.y2 = MIN_VB_ENTRY;
vis = FALSE;
Project_rectangle(&New, Points[0], Points[1], Points[3], Points[2], &vis);
Project_rectangle(&New, Points[4], Points[5], Points[7], Points[6], &vis);
Project_rectangle(&New, Points[0], Points[1], Points[5], Points[4], &vis);
Project_rectangle(&New, Points[2], Points[3], Points[7], Points[6], &vis);
Project_rectangle(&New, Points[1], Points[3], Points[7], Points[5], &vis);
Project_rectangle(&New, Points[0], Points[2], Points[6], Points[4], &vis);
if (vis)
{
if (New.x1 > Project->x1) Project->x1 = New.x1;
if (New.x2 < Project->x2) Project->x2 = New.x2;
if (New.y1 > Project->y1) Project->y1 = New.y1;
if (New.y2 < Project->y2) Project->y2 = New.y2;
*visible = TRUE;
}
}
/*****************************************************************************
*
* FUNCTION : Project_Bounds
*
* ARGUMENTS : Project - Bounding box's projection
* Bounds - Bounding box
* visible - Flag if bounding box is visible
*
* MODIFIED ARGS : Project, visible
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Project a bounding box onto the screen.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Project_Bounds(Project, Bounds, visible)
PROJECT *Project;
BBOX *Bounds;
int *visible;
{
int i;
VECTOR P[8];
for (i = 0; i<8; i++)
{
P[i] = Bounds->Lower_Left;
P[i].x += ((i & 1) ? Bounds->Lengths.x : 0.0);
P[i].y += ((i & 2) ? Bounds->Lengths.y : 0.0);
P[i].z += ((i & 4) ? Bounds->Lengths.z : 0.0);
Transform_Point (&P[i]);
}
Project_Bbox(Project, &P[0], visible);
}
/*****************************************************************************
*
* FUNCTION : Project_Bicubic_Patch
*
* ARGUMENTS : Project - Projection
* Object - Object
*
* MODIFIED ARGS : Project
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Project the bounding sphere of a bicubic patch onto the screen.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Project_Bicubic_Patch(Project, Object)
PROJECT *Project;
OBJECT *Object;
{
BICUBIC_PATCH *patch;
DBL x, y, z, r, a20, a02, a11, a10, a01, a00;
MATRIX A;
patch = (BICUBIC_PATCH *)Object;
/* Set up 4x4 quadric matrix A */
x = patch->Bounding_Sphere_Center.x;
y = patch->Bounding_Sphere_Center.y;
z = patch->Bounding_Sphere_Center.z;
r = 1.0 / (patch->Bounding_Sphere_Radius * patch->Bounding_Sphere_Radius);
A[0][0] = r; A[0][1] = 0.0; A[0][2] = 0.0; A[0][3] = -x*r;
A[1][0] = 0.0; A[1][1] = r; A[1][2] = 0.0; A[1][3] = -y*r;
A[2][0] = 0.0; A[2][1] = 0.0; A[2][2] = r; A[2][3] = -z*r;
A[3][0] = -x*r; A[3][1] = -y*r; A[3][2] = -z*r; A[3][3] = r*(x*x+y*y+z*z) - 1.0;
Project_Vista(&A, NULL, &a20, &a02, &a11, &a10, &a01, &a00);
/* Get vista's bounding rectangle */
Get_Ellipse_Projection(Project, a20, a02, a11, a10, a01, a00);
}
/*****************************************************************************
*
* FUNCTION : Project_Box
*
* ARGUMENTS : Project - Projection
* Object - Object
* visible - Flag if object is visible
*
* MODIFIED ARGS : Project, visible
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Project a box onto the screen.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Project_Box(Project, Object, visible)
PROJECT *Project;
OBJECT *Object;
int *visible;
{
int i;
VECTOR P[8];
BOX *box;
box = (BOX *)Object;
for (i = 0; i<8; i++)
{
P[i] = box->bounds[0];
if (i & 1) P[i].x = box->bounds[1].x;
if (i & 2) P[i].y = box->bounds[1].y;
if (i & 4) P[i].z = box->bounds[1].z;
if (box->Trans != NULL)
MTransPoint(&P[i], &P[i], box->Trans);
Transform_Point (&P[i]);
}
Project_Bbox(Project, &P[0], visible);
}
/*****************************************************************************
*
* FUNCTION : Project_Height_Field
*
* ARGUMENTS : Project - Projection
* Object - Object
* visible - Flag if object is visible
*
* MODIFIED ARGS : Project, visible
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Project the bounding box of a height field onto the screen.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Project_Height_Field(Project, Object, visible)
PROJECT *Project;
OBJECT *Object;
int *visible;
{
int i;
VECTOR P[8];
HEIGHT_FIELD *hfield;
hfield = (HEIGHT_FIELD *)Object;
for (i = 0; i<8; i++)
{
P[i] = hfield->bounding_box->bounds[0];
if (i & 1) P[i].x = hfield->bounding_box->bounds[1].x;
if (i & 2) P[i].y = hfield->bounding_box->bounds[1].y;
if (i & 4) P[i].z = hfield->bounding_box->bounds[1].z;
if (hfield->Trans != NULL)
MTransPoint(&P[i], &P[i], hfield->Trans);
Transform_Point (&P[i]);
}
Project_Bbox(Project, &P[0], visible);
}
/*****************************************************************************
*
* FUNCTION : Project_Triangle
*
* ARGUMENTS : Project - Projection
* Object - Object
* visible - Flag if object is visible
*
* MODIFIED ARGS : Project, visible
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Project a triangle onto the screen.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Project_Triangle(Project, Object, visible)
PROJECT *Project;
OBJECT *Object;
int *visible;
{
int i, vis;
VECTOR P[3];
PROJECT New;
New.x1 = MAX_VB_ENTRY;
New.x2 = MIN_VB_ENTRY;
New.y1 = MAX_VB_ENTRY;
New.y2 = MIN_VB_ENTRY;
P[0] = ((TRIANGLE *)Object)->P1;
P[1] = ((TRIANGLE *)Object)->P2;
P[2] = ((TRIANGLE *)Object)->P3;
for (i = 0; i < 3; i++)
{
Transform_Point(&P[i]);
}
vis = FALSE;
Project_triangle(&New, P[0], P[1], P[2], &vis);
if (vis)
{
if (New.x1 > Project->x1) Project->x1 = New.x1;
if (New.x2 < Project->x2) Project->x2 = New.x2;
if (New.y1 > Project->y1) Project->y1 = New.y1;
if (New.y2 < Project->y2) Project->y2 = New.y2;
*visible = TRUE;
}
}
/*****************************************************************************
*
* FUNCTION : Project_Smooth_Triangle
*
* ARGUMENTS : Project - Projection
* Object - Object
* visible - Flag if object is visible
*
* MODIFIED ARGS : Project, visible
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Project a smooth triangle onto the screen.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Project_Smooth_Triangle(Project, Object, visible)
PROJECT *Project;
OBJECT *Object;
int *visible;
{
int i, vis;
VECTOR P[3];
PROJECT New;
New.x1 = MAX_VB_ENTRY;
New.x2 = MIN_VB_ENTRY;
New.y1 = MAX_VB_ENTRY;
New.y2 = MIN_VB_ENTRY;
P[0] = ((SMOOTH_TRIANGLE *)Object)->P1;
P[1] = ((SMOOTH_TRIANGLE *)Object)->P2;
P[2] = ((SMOOTH_TRIANGLE *)Object)->P3;
for (i = 0; i < 3; i++)
{
Transform_Point(&P[i]);
}
vis = FALSE;
Project_triangle(&New, P[0], P[1], P[2], &vis);
if (vis)
{
if (New.x1 > Project->x1) Project->x1 = New.x1;
if (New.x2 < Project->x2) Project->x2 = New.x2;
if (New.y1 > Project->y1) Project->y1 = New.y1;
if (New.y2 < Project->y2) Project->y2 = New.y2;
*visible = TRUE;
}
}
/*****************************************************************************
*
* FUNCTION : Project_Sphere
*
* ARGUMENTS : Project - Projection
* Object - Oject
*
* MODIFIED ARGS : Project
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Project a sphere onto the screen.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Project_Sphere(Project, Object)
PROJECT *Project;
OBJECT *Object;
{
SPHERE *sphere;
DBL x, y, z, r, a20, a02, a11, a10, a01, a00;
MATRIX A;
sphere = (SPHERE *)Object;
/* Set up 4x4 quadric matrix A0 */
x = sphere->Center.x;
y = sphere->Center.y;
z = sphere->Center.z;
r = 1.0/sphere->Radius_Squared;
A[0][0] = r; A[0][1] = 0.0; A[0][2] = 0.0; A[0][3] = -x*r;
A[1][0] = 0.0; A[1][1] = r; A[1][2] = 0.0; A[1][3] = -y*r;
A[2][0] = 0.0; A[2][1] = 0.0; A[2][2] = r; A[2][3] = -z*r;
A[3][0] = -x*r; A[3][1] = -y*r; A[3][2] = -z*r; A[3][3] = r*(x*x+y*y+z*z) - 1.0;
Project_Vista(&A, sphere->Trans, &a20, &a02, &a11, &a10, &a01, &a00);
/* get vista's bounding rectangle */
Get_Ellipse_Projection(Project, a20, a02, a11, a10, a01, a00);
}
/*****************************************************************************
*
* FUNCTION : Get_Ellipse_Projection
*
* ARGUMENTS : Project - Projection
* a20, a02, a11,
* a10, a01, a00 - Parameters of the quadratic curve
*
* MODIFIED ARGS : Project
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Get the bounding rectangle around an ellipse, i.e. the
* quadratic curve: a20*x*x + a02*y*y + a11*x*y + a10*x + a01*y + a00 = 0
*
* CHANGES
*
* -
*
******************************************************************************/
static void Get_Ellipse_Projection(Project, a20, a02, a11, a10, a01, a00)
PROJECT *Project;
DBL a20, a02, a11, a10, a01, a00;
{
int x1i, x2i, y1i, y2i;
DBL x1, y1, x2, y2;
DBL a, b, c, d, k, k1, k2, k3, k4;
x1 = y1 = -0.5;
x2 = y2 = +0.5;
k1 = a11/(-2.0*a20);
k2 = a10/(-2.0*a20);
k3 = a11/(-2.0*a02);
k4 = a01/(-2.0*a02);
a = a20 + a02*k3*k3 + a11*k3;
b = a10 + 2.0*a02*k3*k4 + a01*k3 + a11*k4;
c = a02*k4*k4 + a01*k4 + a00;
d = b*b - 4.0*a*c;
if (d > 0.0)
{
a = 2.0*a;
d = sqrt(d);
x1 = (-b+d)/a;
x2 = (-b-d)/a;
if (x1>x2)
{
k = x1; x1 = x2; x2 = k;
}
a = a02 + a20*k1*k1 + a11*k1;
b = a01 + 2.0*a20*k1*k2 + a10*k1 + a11*k2;
c = a20*k2*k2 + a10*k2 + a00;
d = b*b - 4.0*a*c;
if (d > 0.0)
{
a = 2.0*a;
d = sqrt(d);
y1 = (-b+d)/a;
y2 = (-b-d)/a;
if (y1>y2)
{
k = y1; y1 = y2; y2 = k;
}
}
}
if (x1 < -0.5) x1 = -0.5;
if (x2 > +0.5) x2 = +0.5;
if (y1 < -0.5) y1 = -0.5;
if (y2 > +0.5) y2 = +0.5;
x1i = Frame.Screen_Width/2 + (int)(Frame.Screen_Width * x1) - 1;
x2i = Frame.Screen_Width/2 + (int)(Frame.Screen_Width * x2) + 1;
y1i = Frame.Screen_Height/2 - (int)(Frame.Screen_Height * y2) - 1;
y2i = Frame.Screen_Height/2 - (int)(Frame.Screen_Height * y1) + 1;
if (x1i > Project->x1) Project->x1 = x1i;
if (x2i < Project->x2) Project->x2 = x2i;
if (y1i > Project->y1) Project->y1 = y1i;
if (y2i < Project->y2) Project->y2 = y2i;
}
/*****************************************************************************
*
* FUNCTION : Project_Vista
*
* ARGUMENTS : A0 - Matrix defining the quadric surface
* Trans - Transformation matrices
* a20, a02, a11,
* a10, a01, a00 - Parameters of the projected quadratic curve
*
* MODIFIED ARGS : a20, a02, a11, a10, a01, a00
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Project a quadric surface onto the screen (perspective projection)
* and get the paramters of the resulting quadratic curve.
*
* CHANGES
*
* -
*
******************************************************************************/
void Project_Vista(A0, Trans, a20, a02, a11, a10, a01, a00)
MATRIX *A0;
TRANSFORM *Trans;
DBL *a20, *a02, *a11, *a10, *a01, *a00;
{
int i, j;
MATRIX Tinv, A1, A2, B, Transposed_Matrix;
DBL k1, k2, k3;
/* Apply object transformations */
if (Trans == NULL)
{
for (i=0;i<4;i++)
{
for (j=0;j<4;j++)
{
A1[i][j] = (*A0)[i][j];
}
}
}
else
{
MTranspose(&Transposed_Matrix, &Trans->inverse);
MTimes(&A1, &Trans->inverse, A0);
MTimes(&A1, &A1, &Transposed_Matrix);
}
/* Transform into viewing system */
MTranspose(&Transposed_Matrix, &WC2VCinv);
MTimes(&A2, &Transposed_Matrix, &A1);
MTimes(&A2, &A2, &WC2VCinv);
/* Calculate quadrics vista */
MIdentity(&Tinv);
Tinv[3][2] = -1.0;
MTranspose(&Transposed_Matrix, &Tinv);
MTimes(&B, &Transposed_Matrix, &A2);
MTimes(&B, &B, &Tinv);
k1 = (B[0][2]+B[2][0])/(-2.0*B[2][2]);
k2 = (B[1][2]+B[2][1])/(-2.0*B[2][2]);
k3 = (B[2][3]+B[3][2])/(-2.0*B[2][2]);
*a20 = B[0][0] + k1*(B[0][2]+B[2][0]) + k1*k1*B[2][2];
*a02 = B[1][1] + k2*(B[1][2]+B[2][1]) + k2*k2*B[2][2];
*a10 = B[0][3]+B[3][0] + k1*(B[2][3]+B[3][2]) + k3*(B[0][2]+B[2][0]) + 2.0*k1*k3*B[2][2];
*a01 = B[1][3]+B[3][1] + k2*(B[2][3]+B[3][2]) + k3*(B[1][2]+B[2][1]) + 2.0*k2*k3*B[2][2];
*a11 = B[0][1]+B[1][0] + k1*(B[1][2]+B[2][1]) + k2*(B[0][2]+B[2][0]) + 2.0*k1*k2*B[2][2];
*a00 = B[3][3] + k3*(B[2][3]+B[3][2]) + k3*k3*B[2][2];
}
/*****************************************************************************
*
* FUNCTION : Transform_Point
*
* ARGUMENTS : P - Point to transform
*
* MODIFIED ARGS : P
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Transform a point from the world coordinate system to the viewer's
* coordinate system.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Transform_Point(P)
VECTOR *P;
{
DBL x,y,z;
x = P->x - O.x;
y = P->y - O.y;
z = P->z - O.z;
P->x = U.x * x + U.y * y + U.z * z;
P->y = V.x * x + V.y * y + V.z * z;
P->z = W.x * x + W.y * y + W.z * z;
}
/*****************************************************************************
*
* FUNCTION : Init_View_Coordinates
*
* ARGUMENTS : none
*
* MODIFIED ARGS : none
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Init the matrices and vectors used to transform a point from
* the world coordinate system to the viewer's coordinate system.
*
* CHANGES
*
* -
*
******************************************************************************/
void Init_View_Coordinates()
{
DBL k, up_length, right_length;
MATRIX A, B;
U = Frame.Camera->Right;
V = Frame.Camera->Up;
W = Frame.Camera->Direction;
VAdd (O, Frame.Camera->Location, Frame.Camera->Direction);
VNormalize(U,U);
VNormalize(V,V);
VNormalize(W,W);
VDot(k, U,V);
if (fabs(k) > EPSILON)
Fatal_Error("Camera: Right-vector not perpendicular to Up-vector.\n");
VDot(k, U,W);
if (fabs(k) > EPSILON)
Fatal_Error("Camera: Right-vector not perpendicular to Direction-vector.\n");
VDot(k, V,W);
if (fabs(k) > EPSILON)
Fatal_Error("Camera: Up-vector not perpendicular to Direction-vector.\n");
VLength (distance, Frame.Camera->Direction);
VLength (up_length, Frame.Camera->Up);
VLength (right_length, Frame.Camera->Right);
VScaleEq (U, 1.0/right_length);
VScaleEq (V, 1.0/up_length);
VScaleEq (W, 1.0/distance);
A[0][0] = U.x; A[0][1] = U.y; A[0][2] = U.z; A[0][3] = 0.0;
A[1][0] = V.x; A[1][1] = V.y; A[1][2] = V.z; A[1][3] = 0.0;
A[2][0] = W.x; A[2][1] = W.y; A[2][2] = W.z; A[2][3] = 0.0;
A[3][0] = 0.0; A[3][1] = 0.0; A[3][2] = 0.0; A[3][3] = 1.0;
B[0][0] = 1.0; B[0][1] = 0.0; B[0][2] = 0.0; B[0][3] = -O.x;
B[1][0] = 0.0; B[1][1] = 1.0; B[1][2] = 0.0; B[1][3] = -O.y;
B[2][0] = 0.0; B[2][1] = 0.0; B[2][2] = 1.0; B[2][3] = -O.z;
B[3][0] = 0.0; B[3][1] = 0.0; B[3][2] = 0.0; B[3][3] = 1.0;
MTimes(&WC2VC, &A, &B);
MInvers(&WC2VCinv, &WC2VC);
}
/*****************************************************************************
*
* FUNCTION : Get_Perspective_Projection
*
* ARGUMENTS : Object - Object to project
* Project - Projection
* infinite - Flag if object is infinite
*
* MODIFIED ARGS : Project
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Get the perspective projection of a single object, i.e.
* the smallest rectangle enclosing the object's image on the screen.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Get_Perspective_Projection(Object, Project, infinite)
OBJECT *Object;
PROJECT *Project;
int infinite;
{
int visible;
METHODS *Methods;
visible = FALSE;
Methods = Object->Methods;
/* If the object is infinite, there's no sense of projecting */
if (!infinite)
{
if ((Methods == &Box_Methods) ||
(Methods == &Smooth_Triangle_Methods) ||
(Methods == &Triangle_Methods) ||
(Methods == &Csg_Height_Field_Methods) ||
(Methods == &Height_Field_Methods))
{
if (Methods == &Box_Methods)
Project_Box(Project, Object, &visible);
if (Methods == &Csg_Height_Field_Methods)
Project_Height_Field(Project, Object, &visible);
if (Methods == &Height_Field_Methods)
Project_Height_Field(Project, Object, &visible);
if (Methods == &Smooth_Triangle_Methods)
Project_Smooth_Triangle(Project, Object, &visible);
if (Methods == &Triangle_Methods)
Project_Triangle(Project, Object, &visible);
}
else
{
Project_Bounds(Project, &Object->Bounds, &visible);
}
}
if (visible)
{
if (Options & ANTIALIAS)
{
/* Increase the rectangle to make sure that nothing will be missed.
For anti-aliased images increase by a larger amount. */
Project->x1 = max (0, Project->x1 - 2);
Project->x2 = min (Frame.Screen_Width-1, Project->x2 + 2);
Project->y1 = max (-1, Project->y1 - 2);
Project->y2 = min (Frame.Screen_Height-1, Project->y2 + 2);
}
else
{
/* Increase the rectangle to make sure that nothing will be missed. */
Project->x1 = max (0, Project->x1 - 1);
Project->x2 = min (Frame.Screen_Width-1, Project->x2 + 1);
Project->y1 = max (0, Project->y1 - 1);
Project->y2 = min (Frame.Screen_Height-1, Project->y2 + 1);
}
}
else
{
if (!infinite)
{
/* Object is invisible (the camera can't see it) */
Project->x1 = Project->y1 = MAX_VB_ENTRY;
Project->x2 = Project->y2 = MIN_VB_ENTRY;
}
}
}
/*****************************************************************************
*
* FUNCTION : Project_Object
*
* ARGUMENTS : Object - Object to project
* Project - Projection
*
* MODIFIED ARGS : Project
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Get the projection of a single object depending on the camera
* used (perspective/orthographic).
*
* CHANGES
*
* -
*
******************************************************************************/
static void Project_Object(Object, Project)
OBJECT *Object;
PROJECT *Project;
{
int infinite;
DBL Volume;
/* Init project fields, assuming the object is visible! */
Project->x1 = Project->y1 = MIN_VB_ENTRY;
Project->x2 = Project->y2 = MAX_VB_ENTRY;
BOUNDS_VOLUME(Volume, Object->Bounds);
infinite = (Volume > INFINITE_VOLUME);
Get_Perspective_Projection(Object, Project, infinite);
Print_Point(POINT_MOD);
}
/*****************************************************************************
*
* FUNCTION : Project_Bounding_Slab
*
* ARGUMENTS : Project - Projection
* Tree - Current node/leaf
* Object - Node/leaf in bounding slab hierarchy
*
* MODIFIED ARGS : Project, Tree
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Project the bounding slab hierarchy onto the screen and thus create
* the vista buffer hierarchy.
*
* CHANGES
*
* -
*
******************************************************************************/
static void Project_Bounding_Slab(Project, Tree, Object)
PROJECT *Project;
PROJECT_TREE_NODE **Tree;
OBJECT *Object;
{
unsigned short int i;
COMPOSITE *Comp;
PROJECT Temp;
PROJECT_TREE_LEAF *Leaf;
PROJECT_TREE_NODE New;
if (Object->Type & BOUNDING_OBJECT)
{
/* Current object is a bounding object, i.e. a node in the slab tree. */
Comp = (COMPOSITE *)Object;
/* First, init new entry. */
New.Entries = 0;
New.Object = Object;
New.Project.x1 = New.Project.y1 = MAX_VB_ENTRY;
New.Project.x2 = New.Project.y2 = MIN_VB_ENTRY;
/* Allocate temporary memory for node/leaf entries. */
if ((New.Entry = (PROJECT_TREE_NODE **)malloc(Comp->Entries*sizeof(PROJECT_TREE_NODE *))) == NULL)
{
Fatal_Error("temporary tree entry");
}
/* This is no leaf, it's a node. */
New.is_leaf = FALSE;
/* Second, get new entry, i.e. project node's entries. */
for (i = 0; i < Comp->Entries; i++)
{
New.Entry[i] = NULL;
Project_Bounding_Slab(&Temp, &New.Entry[New.Entries], Comp->Objects[i]);
/* Use only visible entries */
if (New.Entry[New.Entries] != NULL)
{
New.Project.x1 = min(New.Project.x1, Temp.x1);
New.Project.x2 = max(New.Project.x2, Temp.x2);
New.Project.y1 = min(New.Project.y1, Temp.y1);
New.Project.y2 = max(New.Project.y2, Temp.y2);
New.Entries++;
}
}
/* If there are any visible entires, we'll use them. */
if (New.Entries > 0)
{
/* If there's only one entry, we won't need a new node. */
if (New.Entries == 1)
{
*Tree = New.Entry[0];
*Project = New.Project;
}
else
{
/* Allocate memory for new node in the vista tree (never freed!) */
*Tree = (PROJECT_TREE_NODE *)VB_malloc(sizeof(PROJECT_TREE_NODE));
if (*Tree == NULL)
{
Fatal_MAError("vista tree node");
}
**Tree = New;
/* Allocate memory for node/leaf entries. */
(*Tree)->Entry = (PROJECT_TREE_NODE **)VB_malloc(New.Entries*sizeof(PROJECT_TREE_NODE *));
if ((*Tree)->Entry == NULL)
{
Fatal_MAError("vista tree node");
}
memcpy((*Tree)->Entry, New.Entry, New.Entries*sizeof(PROJECT_TREE_NODE *));
*Project = New.Project;
}
}
/* Get rid of temporary node/leaf entries. */
free(New.Entry);
}
else
{
/* Current object is a normal object, i.e. a leaf in the slab tree */
/* Get object's projection */
Project_Object(Object, Project);
/* Is the object visible? */
if ((Project->x1 <= Project->x2) && (Project->y1 <= Project->y2))
{
/* Allocate memory for new leaf in the vista tree (never freed!) */
*Tree = (PROJECT_TREE_NODE *)VB_malloc(sizeof(PROJECT_TREE_LEAF));
if (*Tree == NULL)
{
Fatal_MAError("vista tree leaf");
}
/* Init new leaf */
Leaf = (PROJECT_TREE_LEAF *)(*Tree);
Leaf->Object = Object;
Leaf->Project = *Project;
/* Yes, this is a leaf */
Leaf->is_leaf = TRUE;
}
}
}
/*****************************************************************************
*
* FUNCTION : Build_Vista_Tree
*
* ARGUMENTS : none
*
* MODIFIED ARGS : none
*
* RETURN VALUE : none
*
* AUTHOR : Dieter Bayer, May 1994
*
* DESCRIPTION
*
* Build the vista tree, i.e. the 2d representation of the bounding slab
* hierarchy in image space.
*
* This only works for perspective and orthographic cameras.
*
* CHANGES
*
* -
*
******************************************************************************/
void Build_Vista_Tree()
{
PROJECT Project;
Root_Vista = NULL;
if (Use_Slabs)
{
fprintf(stderr, "Building vista buffer");
Begin_Point();
Project_Bounding_Slab(&Project, &Root_Vista, Root_Object);
End_Point();
}
}
#endif